#include <igraph.h>
#include <string.h>
#include <stdlib.h>
#include <unordered_map>
#include <fstream>
#include <sstream>
#include <string>
#include <iostream>
#include <chrono>
#include <iosfwd>
#include <map>
#include <algorithm>
#include <cctype>
#include <random>
#include <functional>

using std::chrono::duration_cast;
using std::chrono::microseconds;
using std::chrono::steady_clock;

typedef std::unordered_map<std::string, std::string> RowMap;
RowMap g_nodesMap;

int g_idVecToRemove = -1;
int g_addonsOnly = 1;
int g_uid = 0;
std::string g_cmd;
std::multimap<float, std::string> g_sortedPPR;
std::vector<int> g_addons_vector;

FILE *ifile;
FILE *ofile;
FILE *ofileAfterRemoval;
FILE *afile;
FILE *degreefile;
FILE *recallfile;

void replaceAll(std::string& str, const std::string& from, const std::string& to) {
  if (from.empty())
    return;
  size_t start_pos = 0;
  while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
    str.replace(start_pos, from.length(), to);
    start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
  }
}

bool is_number(const std::string& s)
{
  return !s.empty() && std::find_if(s.begin(),
    s.end(), [](char c) { return !std::isdigit(c); }) == s.end();
}

std::string nodeNamefromId(const igraph_t *g, FILE *f, int nodeId)
{
  igraph_vector_t gtypes, vtypes, etypes;
  igraph_strvector_t gnames, vnames, enames;
  long int i;

  igraph_vector_t vec;
  igraph_strvector_t svec;
  long int j;

  igraph_vector_init(&gtypes, 0);
  igraph_vector_init(&vtypes, 0);
  igraph_vector_init(&etypes, 0);
  igraph_strvector_init(&gnames, 0);
  igraph_strvector_init(&vnames, 0);
  igraph_strvector_init(&enames, 0);

  igraph_cattribute_list(g, &gnames, &gtypes, &vnames, &vtypes,
    &enames, &etypes);

  if (nodeId < 0)
    return "";

  return VAS(g, STR(vnames, 0), nodeId);
}

int igraphNodeIDfromNodeMapID(const igraph_t *g, FILE *f, int NodeName)
{
  igraph_vector_t gtypes, vtypes, etypes;
  igraph_strvector_t gnames, vnames, enames;
  long int i;

  igraph_vector_t vec;
  igraph_strvector_t svec;
  long int j;

  igraph_vector_init(&gtypes, 0);
  igraph_vector_init(&vtypes, 0);
  igraph_vector_init(&etypes, 0);
  igraph_strvector_init(&gnames, 0);
  igraph_strvector_init(&vnames, 0);
  igraph_strvector_init(&enames, 0);

  igraph_cattribute_list(g, &gnames, &gtypes, &vnames, &vtypes,
    &enames, &etypes);

  igraph_strvector_init(&svec, 0);
  igraph_cattribute_VASV(g, STR(vnames, 0), igraph_vss_all(), &svec);
  for (i = 0; i < igraph_vcount(g); i++) {
    //const char *str = VAS(g, STR(vnames, 0), i);

    //std::string str1 = VAS(g, STR(vnames, 0), i);
    std::string strCurNodeName = STR(svec, i);
    if (is_number(strCurNodeName))
    {
      int CurNodeName = std::stoi(strCurNodeName);
      if (CurNodeName == NodeName)
      {
        return i;
      }
    }
  }

  return -1;
}

int print_attributes(const igraph_t *g, FILE *f) {

  igraph_vector_t gtypes, vtypes, etypes;
  igraph_strvector_t gnames, vnames, enames;
  long int i;

  igraph_vector_t vec;
  igraph_strvector_t svec;
  long int j;

  igraph_vector_init(&gtypes, 0);
  igraph_vector_init(&vtypes, 0);
  igraph_vector_init(&etypes, 0);
  igraph_strvector_init(&gnames, 0);
  igraph_strvector_init(&vnames, 0);
  igraph_strvector_init(&enames, 0);

  igraph_cattribute_list(g, &gnames, &gtypes, &vnames, &vtypes,
    &enames, &etypes);

  /* Graph attributes */
  for (i = 0; i < igraph_strvector_size(&gnames); i++) {
    printf("%s=", STR(gnames, i));
    if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) {
      igraph_real_printf(GAN(g, STR(gnames, i)));
      putchar(' ');
    }
    else {
      printf("\"%s\" ", GAS(g, STR(gnames, i)));
    }
  }
  printf("\n");

  for (i = 0; i < igraph_vcount(g); i++) {
    long int j;
    fprintf(f, "Vertex %li: ", i);
    for (j = 0; j < igraph_strvector_size(&vnames); j++) {
      fprintf(f, "%s=", STR(vnames, j));
      if (VECTOR(vtypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) {
        igraph_real_printf(VAN(g, STR(vnames, j), i));
        putchar(' ');
      }
      else {
        fprintf(f, "%s", VAS(g, STR(vnames, j), i));
      }
    }
    fprintf(f, "\n");
  }

  for (i = 0; i < igraph_ecount(g); i++) {
    long int j;
    fprintf(f, "Edge %li (%i-%i): ", i, (int)IGRAPH_FROM(g, i), (int)IGRAPH_TO(g, i));
    for (j = 0; j < igraph_strvector_size(&enames); j++) {
      fprintf(f, "%s=", STR(enames, j));
      if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) {
        igraph_real_printf(EAN(g, STR(enames, j), i));
        putchar(' ');
      }
      else {
        fprintf(f, "\"%s\" ", EAS(g, STR(enames, j), i));
      }
    }
    fprintf(f, "\n");
  }

  ///* Check vector-based query functions */
  //igraph_vector_init(&vec, 0);
  //igraph_strvector_init(&svec, 0);
  //
  //for (j = 0; j<igraph_strvector_size(&vnames); j++) {
  //  if (VECTOR(vtypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) {
  //    igraph_cattribute_VANV(g, STR(vnames, j), igraph_vss_all(), &vec);
  //    for (i = 0; i<igraph_vcount(g); i++) {
  //      igraph_real_t num = VAN(g, STR(vnames, j), i);
  //      if (num != VECTOR(vec)[i] &&
  //        (!isnan(num) || !isnan(VECTOR(vec)[i]))) {
  //        exit(51);
  //      }
  //    }
  //  }
  //  else {
  //    igraph_cattribute_VASV(g, STR(vnames, j), igraph_vss_all(), &svec);
  //    for (i = 0; i<igraph_vcount(g); i++) {
  //      const char *str = VAS(g, STR(vnames, j), i);
  //      if (strcmp(str, STR(svec, i))) {
  //        exit(52);
  //      }
  //    }
  //  }
  //}
  //
  //for (j = 0; j<igraph_strvector_size(&enames); j++) {
  //  if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) {
  //    igraph_cattribute_EANV(g, STR(enames, j),
  //      igraph_ess_all(IGRAPH_EDGEORDER_ID), &vec);
  //    for (i = 0; i<igraph_ecount(g); i++) {
  //      igraph_real_t num = EAN(g, STR(enames, j), i);
  //      if (num != VECTOR(vec)[i] &&
  //        (!isnan(num) || !isnan(VECTOR(vec)[i]))) {
  //        exit(53);
  //      }
  //    }
  //  }
  //  else {
  //    igraph_cattribute_EASV(g, STR(enames, j),
  //      igraph_ess_all(IGRAPH_EDGEORDER_ID), &svec);
  //    for (i = 0; i<igraph_ecount(g); i++) {
  //      const char *str = EAS(g, STR(enames, j), i);
  //      if (strcmp(str, STR(svec, i))) {
  //        exit(54);
  //      }
  //    }
  //  }
  //}
  //igraph_strvector_destroy(&svec);
  //igraph_vector_destroy(&vec);

  igraph_strvector_destroy(&enames);
  igraph_strvector_destroy(&vnames);
  igraph_strvector_destroy(&gnames);
  igraph_vector_destroy(&etypes);
  igraph_vector_destroy(&vtypes);
  igraph_vector_destroy(&gtypes);

  return 0;
}


void print_vector(igraph_vector_t *v, FILE *f) {
  long int i;
  for (i = 0; i < igraph_vector_size(v); i++) {
    float m = VECTOR(*v)[i] * 1;
    //printf("\"%s\" ", VAS(&g, STR(vnames, 0), 1));
    fprintf(f, " %g\n", m);

  }
  //igraph_vector_print(v);
  fprintf(f, "\n");
}

void print_nodes_connection_state(FILE *f, igraph_t *g, igraph_integer_t from, igraph_integer_t to)
{
  igraph_bool_t conn = 2;
  igraph_are_connected(g, (igraph_integer_t)from,
    (igraph_integer_t)to, &conn);

  if (conn == 1)
    std::cout << "node " << from << " IS connected to node " << to << std::endl;
  else if (conn == 0)
    std::cout << "node " << from << " NOT connected to node " << to << std::endl;
  else
    std::cout << "Error" << std::endl;
}


void print_ppr_data(FILE *f, igraph_t *g, igraph_vs_t *vsAddonsReset, char* str)
{
  fprintf(f, "%s\n", str);
  igraph_vit_t vit;
  igraph_vit_create(g, *vsAddonsReset, &vit);
  while (!IGRAPH_VIT_END(vit)) {
    fprintf(f, "%li ", (long int)IGRAPH_VIT_GET(vit));
    fprintf(f, "%s ", nodeNamefromId(g, stdout, (long int)IGRAPH_VIT_GET(vit)).c_str());
    fprintf(f, " %s\n", g_nodesMap[nodeNamefromId(g, stdout, (long int)IGRAPH_VIT_GET(vit))].c_str());
    g_addons_vector.push_back((int)IGRAPH_VIT_GET(vit));
    IGRAPH_VIT_NEXT(vit);
  }
  igraph_vit_destroy(&vit);
}

void print_ppr_vector(igraph_vector_t *v, FILE *f, igraph_strvector_t *vnames, igraph_t *g, char* str, igraph_vs_t *vsAddonsReset, bool bPrintRecall) {
  long int i;
  igraph_vector_t v_sorted;
  igraph_vector_copy(&v_sorted, v);
  igraph_vector_sort(&v_sorted);
  //igraph_real_t *ptr;
  //ptr = (igraph_real_t*)malloc(igraph_vector_size(v) * sizeof(igraph_real_t));
  //igraph_vector_copy_to(v, ptr);
  //fprintf(f, " ---------------START-%s--------------------\n", str);
  //fprintf(f, "nodeId \tPageRank \tNodeName\n", str);
  std::string our_name;
  std::string our_fullname;
  float ourAddonPagerank = 0;
  std::vector<float> pr_vector;
  for (i = 0; i < igraph_vector_size(v); i++) {
    float pagerank = VECTOR(*v)[i] * 1000;
    if (g_addonsOnly)
    {
      std::string nodeName = VAS(g, STR(*vnames, 0), i);
      if (nodeName.at(0) != '2' && nodeName.at(0) != '3' && nodeName.at(0) != '4')
      {
        continue;
      }
      //g_sortedPPR.emplace(pagerank, VAS(g, STR(*vnames, 0), i));
    }

    fprintf(f, " %g\t", pagerank);
    fprintf(f, "%s\t", VAS(g, STR(*vnames, 0), i));
    std::string name = VAS(g, STR(*vnames, 0), i);
    std::string addonFullName = g_nodesMap[name];
    if (i == g_idVecToRemove)
    {
      long int pos = 0;
      ourAddonPagerank = pagerank;
      our_name = name;
      our_fullname = addonFullName;
      igraph_vector_binsearch(&v_sorted, VECTOR(*v)[i], &pos);
      //pos = igraph_vector_size(v) - pos;
      //std::cout << "\nOur addon " << name << " current pagerank " << pagerank << " position " << pos << " out of " << igraph_vector_size(v) << std::endl;
      //std::cout << "Addon name: " << addonFullName << std::endl;
      addonFullName.insert(0, "YYY-");
    }
    else if (std::find(g_addons_vector.begin(), g_addons_vector.end(), i) != g_addons_vector.end())
    {
      addonFullName.insert(0, "ZZZ-");
    }
    fprintf(f, " %s\n", addonFullName.c_str());

    pr_vector.push_back(pagerank);
  }

  // print content:
  if (g_addonsOnly && bPrintRecall)
  {
    std::string tmp = g_cmd;
    replaceAll(tmp, "-u rnd", "-u " + std::to_string(g_uid));
    replaceAll(tmp, "-a rnd", "-a " + nodeNamefromId(g, stdout, g_idVecToRemove));

    std::sort(pr_vector.begin(), pr_vector.end(), std::greater<float>());
    auto x = std::find(pr_vector.begin(), pr_vector.end(), ourAddonPagerank);
    //std::cout << "location of " << ourAddonPagerank << " is "<< std::distance(pr_vector.begin(), x) + 1 << std::endl;
    std::cout << "\nOur addon " << our_name << " current pagerank " << ourAddonPagerank << " position " << std::distance(pr_vector.begin(), x) + 1 << " out of " << pr_vector.size() << " addons." << std::endl;
    int dist = std::distance(pr_vector.begin(), x) + 1;
    int effectiveRank = dist - (int)g_addons_vector.size();
    fprintf(recallfile, "%d\t%d\t%g\t%s\t%s\t%d\t%s\n", effectiveRank, pr_vector.size(), ourAddonPagerank, our_fullname.c_str(), our_name.c_str(), g_uid, tmp.c_str());
    fprintf(stdout, "%d\t%d\t%g\t%s\t%s\t%d\t%s\n", effectiveRank, pr_vector.size(), ourAddonPagerank, our_fullname.c_str(), our_name.c_str(), g_uid, tmp.c_str());

    //std::cout << "mymultimap contains:";
    //int j = 0;
    //for (auto it = g_sortedPPR.rbegin(); it != g_sortedPPR.rend(); ++it)
    //{
    //  
    //  //std::cout << it->first << '\t' << it->second << '\n';
    //  j++;
    //  //if (it->first == ourAddonPagerank)
    //  //if (it->second.compare(name) == 0)
    //  float pr = it->first;
    //  std::string pn = it->second;
    //  if (pn.compare(name) == 0)
    //  {
    //    //std::cout << "loc " << std::distance(g_sortedPPR.begin(), it);
    //    std::cout << "Addon pagerank position relative to other addons is " << j << " , addon node ID:" << it->second << std::endl;
    //    break;
    //  }
    //}
    //std::cout << '\n';
  }
}


bool readNodesMap(RowMap &row, std::string fileName)
{
  std::ifstream filehandle(fileName.c_str());

  std::string line = "";
  while (std::getline(filehandle, line))
  {
    std::stringstream line_ss(line);
    std::string column = "";
    unsigned int index = 0;
    std::string nodeId;
    std::string nodeName;
    while (std::getline(line_ss, column, '\t'))
    {
      if (index == 0)
      {
        nodeId = column;
      }
      if (index == 1)
      {
        nodeName = column;
      }
      index++;
    }
    row[nodeId] = nodeName;
  }
  return false;
}

void print_and_destroy_cliques(igraph_vector_ptr_t *cliques) {
  int i;
  for (i = 0; i < igraph_vector_ptr_size(cliques); i++) {
    igraph_vector_t *v = (igraph_vector_t *)VECTOR(*cliques)[i];
    igraph_vector_print(v);
    igraph_vector_destroy(v);
    igraph_free(v);
  }
}

int pickRandomNumber(int a, int b)
{
  // construct a trivial random generator engine from a time-based seed:
  unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
  std::default_random_engine generator(seed);
  std::uniform_int_distribution<int> distribution(a, b - 1);
  return distribution(generator);
}

int pickRandomNumberNotEqual(int a, int b, std::vector<std::string> vec, igraph_t *g)
{
  int rnd = 0;
  std::string strRnd;
  int addonsNumber = 0;
  do
  {
    // construct a trivial random generator engine from a time-based seed:
    unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
    std::default_random_engine generator(seed);
    std::uniform_int_distribution<int> distribution(a, b - 1);
    rnd = distribution(generator);

    strRnd = std::to_string(rnd);
    //find all the nodes connected to uid --> all it's addons
    if (std::find(vec.begin(), vec.end(), strRnd) != vec.end())
      continue;

    int vid = igraphNodeIDfromNodeMapID(g, stdout, rnd);
    igraph_vs_t vsUid;
    igraph_vs_1(&vsUid, vid);
    igraph_vector_ptr_t vptrAddons;
    igraph_vector_ptr_init(&vptrAddons, 0);

    igraph_neighborhood(g, &vptrAddons, vsUid, 1, IGRAPH_ALL);
    igraph_vector_t *vAddons = (igraph_vector_t *)VECTOR(vptrAddons)[0];
    //remove first element since it is the uid node itself
    igraph_vector_remove(vAddons, 0);
    igraph_vs_t vsAddonsReset;
    igraph_vs_vector_copy(&vsAddonsReset, vAddons);

    addonsNumber = igraph_vector_size(vAddons);
    if (addonsNumber < 2)
    {
      std::cout << "Only " << addonsNumber << " addons, continue looking...\n";
      //continue;
    }

    igraph_vector_ptr_destroy_all(&vptrAddons);

  } while (std::find(vec.begin(), vec.end(), strRnd) != vec.end() || addonsNumber < 2);

  return rnd;
}

int igraph_degree_1(const igraph_t *igraph, int vertex) {
  igraph_real_t v;
  igraph_vector_t vp;
  igraph_vector_view(&vp, &v, 1);
  igraph_degree(igraph, &vp, igraph_vss_1(vertex), IGRAPH_ALL, IGRAPH_LOOPS);
  return v;
}

void printAllNodesDegrees(FILE *f, igraph_t *g)
{
  igraph_vector_t types;
  igraph_vector_init(&types, 0);
  igraph_degree(g, &types, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS);

  igraph_vector_print(&types);
  for (int i = 0; i < igraph_vector_size(&types); i++) {
    int nodeDegree = VECTOR(types)[i];
    std::cout << nodeNamefromId(g, f, i) << " degree " << nodeDegree << std::endl;
  }
}

std::vector<std::string> removeNodes(FILE *f, igraph_t *g, int maxDegree, int maxNodesNum)
{
  std::vector<std::string> deletedNodes;
  if (maxDegree < 0 && maxNodesNum < 0)
    return deletedNodes;

  int minDegree = -1;
  igraph_vector_t sortedDegrees;
  igraph_vector_init(&sortedDegrees, 0);
  igraph_degree(g, &sortedDegrees, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS);
  igraph_vector_sort(&sortedDegrees);
  igraph_vector_reverse(&sortedDegrees);
#ifdef _DEBUG
  igraph_vector_print(&sortedDegrees);
#endif // DEBUG
  if (maxNodesNum > 0 && maxNodesNum - 1 < igraph_vector_size(&sortedDegrees))
  {
    //find the degree of maxNodesNum's element
    minDegree = VECTOR(sortedDegrees)[maxNodesNum - 1];
    std::cout << "Min degree of not deleted nodes " << minDegree << std::endl;
  }

  igraph_vector_t types;
  igraph_vector_init(&types, 0);
  igraph_degree(g, &types, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS);
#ifdef _DEBUG
  printAllNodesDegrees(f, g);
#endif // DEBUG

  igraph_vector_t toDelete;
  igraph_vector_init(&toDelete, 0);
  int removed = 0;
  //igraph_vector_print(&types);
  for (int i = 0; i < igraph_vector_size(&types); i++) {
    int nodeDegree = VECTOR(types)[i];
    if (nodeDegree > maxDegree)
    {
      //if we want to remove only maxNodesNum number of nodes, cut according to minDegree, calculated above
      if (minDegree > 0 && nodeDegree < minDegree)
        continue;

      igraph_vector_push_back(&toDelete, i);
      //std::cout << "Will delete " << nodeNamefromId(g, f, i) << " degree " << nodeDegree << " " << g_nodesMap[nodeNamefromId(g, f, i)] << std::endl;
      fprintf(f, "Will delete %s degree %d %s\n", nodeNamefromId(g, f, i).c_str(), nodeDegree, g_nodesMap[nodeNamefromId(g, f, i)].c_str());
      deletedNodes.push_back(nodeNamefromId(g, f, i));
      removed++;
    }
    else
    {
      //Needs to calculate new vector each time
      //std::cout << nodeNamefromId(g, f, i) << " degree " << nodeDegree << std::endl;
    }
  }

  if (igraph_delete_vertices(g, igraph_vss_vector(&toDelete)) != 0)
    std::cout << "Could not delete vertices" << std::endl;
  else
    std::cout << "Deleted " << removed << " vertices" << std::endl;

  //Verify these nodes were deleted
  //igraph_vector_t typesAfterRemoval;
  //igraph_vector_init(&typesAfterRemoval, 0);
  //igraph_degree(g, &typesAfterRemoval, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS);
  //igraph_vector_print(&typesAfterRemoval);
  //igraph_vector_sort(&typesAfterRemoval);
  //igraph_vector_print(&typesAfterRemoval);
#ifdef _DEBUG
  printAllNodesDegrees(f, g);
#endif // DEBUG

  return deletedNodes;
}

int main(int argc, char* argv[]) {
  igraph_t g;
  igraph_vector_t gtypes, vtypes, etypes;
  igraph_strvector_t gnames, vnames, enames;
  long int i;
  igraph_vector_t y;
  igraph_strvector_t id;

  std::string ipath = R"(C:\addonsproject\test.txt)";
  //auto ipath = R"(C:\addonsproject\graph_input_uniq.txt)";
  std::string opath = R"(C:\addonsproject\PPR.txt)";
  std::string recallpath = R"(recall.txt)";
  std::string opath2 = R"(C:\addonsproject\PPRAfterRemoval.txt)";
  std::string degreePath = R"(RemovedAddonsDegree.txt)";
  std::string nodesMapPath = R"(C:\addonsproject\nodesMap.txt)";
  std::string userId;
  std::string addonId;
  std::string dirName;
  int maxDegree = -1;
  int topNodesNumToRemove = -1;
  bool bDebug = false;
  int directed = 0;
  int numberOfRuns = 200;

  igraph_pagerank_power_options_t power_options;

  std::cout << "Start\n";
  /* turn on attribute handling */
  igraph_i_set_attribute_table(&igraph_cattribute_table);

  // Loop through each argument and print its number and value
  std::string cmd;
  for (int nArg = 0; nArg < argc; nArg++)
  {
    std::cout << argv[nArg] << " ";
    cmd.append(argv[nArg]);
    cmd.append(" ");
  }

  g_cmd = cmd;

  std::cout << "\n";

  if (argc < 13) {
    std::cout << "Usage is -dir <current directory> -i <input graph> -o1 <ppr output file> -o2 <ppr after addon removal output file>\n"
      << "-m <nodes map file> -u <user id node name(nodeMap ID) (it's will be the ppr vector)> -a (nodeMap ID)<addon node number to be removed from ppr vector>\n"
      << "-maxdegree <remove all nodes with degree higher than this> -topnodes <remove ~topnodes with highest degree>\n";
    std::cout << "The program will continue with default params\n";
  }
  else { // if we got enough parameters...
    for (int i = 1; i < argc; i++) {
      if (i + 1 != argc)
      {
        if (strcmp(argv[i], "-i") == 0) {
          ipath = argv[i + 1];
          ipath.insert(0, dirName);
        }
        else if (strcmp(argv[i], "-o1") == 0) {
          opath = argv[i + 1];
          opath.insert(0, dirName);
        }
        else if (strcmp(argv[i], "-o2") == 0) {
          opath2 = argv[i + 1];
          opath2.insert(0, dirName);
        }
        else if (strcmp(argv[i], "-m") == 0) {
          nodesMapPath = argv[i + 1];
          nodesMapPath.insert(0, dirName);
        }
        else if (strcmp(argv[i], "-u") == 0) {
          userId = argv[i + 1];
        }
        else if (strcmp(argv[i], "-a") == 0) {
          addonId = argv[i + 1];
        }
        else if (strcmp(argv[i], "-dir") == 0) {
          dirName = argv[i + 1];
          dirName += "\\";
        }
        else if (strcmp(argv[i], "-maxdegree") == 0) {
          std::string tmp = argv[i + 1];
          maxDegree = std::stoi(tmp);
        }
        else if (strcmp(argv[i], "-nruns") == 0) {
          std::string tmp = argv[i + 1];
          numberOfRuns = std::stoi(tmp);
        }
        else if (strcmp(argv[i], "-topnodes") == 0) {
          std::string tmp = argv[i + 1];
          topNodesNumToRemove = std::stoi(tmp);
        }
        else if (strcmp(argv[i], "-directed") == 0) {
          //std::string tmp = argv[i + 1];
          directed = 1;
        }
        else if (strcmp(argv[i], "-addonsonly") == 0) {
          //std::string tmp = argv[i + 1];
          g_addonsOnly = 1;
        }
        else if (strcmp(argv[i], "-debug") == 0) {
          bDebug = true;
          std::cout << "\nDebug mode!\n";
        }
        i++;
      }
    }
  }

  recallpath.insert(0, dirName);

  std::cout << "Opening graph: " << ipath << std::endl;
  ifile = fopen(ipath.c_str(), "r");
  if (ifile == 0) {
    std::cout << "Failed opening file" << ipath;
    return 10;
  }

  ofile = fopen(opath.c_str(), "w");
  if (ofile == 0) {
    std::cout << "Failed opening file" << opath;
    return 10;
  }

  ofileAfterRemoval = fopen(opath2.c_str(), "w");
  if (ofileAfterRemoval == 0) {
    std::cout << "Failed opening file" << opath2;
    return 10;
  }

  degreefile = fopen(degreePath.c_str(), "w");
  if (degreefile == 0) {
    std::cout << "Failed opening file" << degreePath;
    return 10;
  }

  recallfile = fopen(recallpath.c_str(), "a");
  if (recallfile == 0) {
    std::cout << "Failed opening file" << recallpath;
    return 10;
  }

  //auto apath = R"(C:\addonsproject\attributesPPR.txt)";
  //afile = fopen(apath, "w");
  //if (afile == 0) {
  //  return 10;
  //}

  auto startProgramRun = steady_clock::now();
  auto start = steady_clock::now();

#ifndef _DEBUG
  readNodesMap(g_nodesMap, nodesMapPath);
#endif

  auto end = steady_clock::now();

  std::cout << "Loading nodes map from " << nodesMapPath << " took "
    // duration_cast is required to avoid accidentally losing precision.
    << duration_cast<std::chrono::seconds>(end - start).count()
    << " sec.\n";

  start = steady_clock::now();
  std::cout << "Loading graph... (can take a few minutes) \n";

  igraph_read_graph_ncol(&g, ifile, NULL, 1, IGRAPH_ADD_WEIGHTS_IF_PRESENT, directed);

  if (igraph_is_directed(&g))
    std::cout << "Graph IS directed\n";
  else
    std::cout << "Graph is NOT directed\n";

  end = steady_clock::now();
  std::cout << "Loading graph took "
    // duration_cast is required to avoid accidentally losing precision.
    << duration_cast<std::chrono::seconds>(end - startProgramRun).count()
    << " sec.\n";

  igraph_vector_init(&gtypes, 0);
  igraph_vector_init(&vtypes, 0);
  igraph_vector_init(&etypes, 0);
  igraph_strvector_init(&gnames, 0);
  igraph_strvector_init(&vnames, 0);
  igraph_strvector_init(&enames, 0);

  igraph_cattribute_list(&g, &gnames, &gtypes, &vnames, &vtypes,
    &enames, &etypes);

#ifdef _DEBUG
  print_attributes(&g, stdout);
  bDebug = true;
#endif

  //remove high degree node
  std::vector<std::string> deletedNodes = removeNodes(degreefile, &g, maxDegree, topNodesNumToRemove);

  do
  {
    int uid = 0;
#ifdef _DEBUG
    uid = pickRandomNumberNotEqual(1000001, 1000008, deletedNodes, &g);
#else
    uid = pickRandomNumberNotEqual(1000001, 1907845, deletedNodes, &g);
#endif

    g_uid = uid;

    int vid = igraphNodeIDfromNodeMapID(&g, stdout, uid);
    std::cout << "User ID node, igraph ID:" << vid << " ,nodeMap ID: " << uid << " ,full name: " << g_nodesMap[std::to_string(uid)].c_str() << std::endl;

    igraph_vs_t vsUid;
    igraph_vs_1(&vsUid, vid);
    igraph_vector_ptr_t vptrAddons;
    igraph_vector_ptr_init(&vptrAddons, 0);

    //find all the nodes connected to uid --> all it's addons
    igraph_neighborhood(&g, &vptrAddons, vsUid, 1, IGRAPH_ALL);
    igraph_vector_t *vAddons = (igraph_vector_t *)VECTOR(vptrAddons)[0];
    //remove first element since it is the uid node itself
    igraph_vector_remove(vAddons, 0);
    igraph_vs_t vsAddonsReset;
    igraph_vs_vector_copy(&vsAddonsReset, vAddons);


    igraph_arpack_options_t arpack_options;
    igraph_vector_t pprResult;
    igraph_vector_init(&pprResult, 0);
    igraph_arpack_options_init(&arpack_options);

    /* Personalized PageRank */

    //pick one of 1 to (size of PPR vector) cell
    int addonsNumber = 0;
    addonsNumber = igraph_vector_size(vAddons);
    if (addonsNumber < 2)
    {
      std::cout << "Only  " << addonsNumber << " addons, exiting..." << '\n';
      return 0;
    }
    int elemPos = pickRandomNumber(0, addonsNumber);
    g_idVecToRemove = VECTOR(*vAddons)[elemPos];
    //find the actual node id in graph
    if (!addonId.empty())
    {
      if (is_number(addonId))
      {
        long pos = 0;
        g_idVecToRemove = igraphNodeIDfromNodeMapID(&g, stdout, std::stoi(addonId));
        if (igraph_vector_search(vAddons, 0, g_idVecToRemove, &pos))
          elemPos = pos;
        else
          std::cout << "Could not find " << g_idVecToRemove << " in the vector\n";
      }
    }

    //print_ppr_data(stdout, &g, &vsAddonsReset, "Original PPR vector");

    /*Now, remove one of the PPR vector nodes*/
    //NEW PPR
    //(Step 1 - delete one addon from the personalization vector)
    igraph_vector_remove(vAddons, elemPos);
    igraph_vs_t vsAddonsResetNew;
    igraph_vs_vector_copy(&vsAddonsResetNew, vAddons);
    std::cout << "Removed node igraph ID: " << g_idVecToRemove << " ,at position " << elemPos << " ,nodeMap ID:" << nodeNamefromId(&g, stdout, g_idVecToRemove) << " ,full name: " << g_nodesMap[nodeNamefromId(&g, stdout, g_idVecToRemove)] << std::endl;

    //print_ppr_data(stdout, &g, &vsAddonsResetNew, "New PPR vector");

    print_nodes_connection_state(stdout, &g, vid, g_idVecToRemove);

    //Delete edge between the removed addon node and user node
    //(Step 2 - delete edge between user and addon)
    igraph_es_t es;
    igraph_es_path_small(&es, directed, vid, g_idVecToRemove, -1);
    int delRes = igraph_delete_edges(&g, es);
    //std::cout << "Delete edge " << vid << "-" << g_idVecToRemove << '\n';

    print_nodes_connection_state(stdout, &g, vid, g_idVecToRemove);

    start = steady_clock::now();

    igraph_personalized_pagerank_vs(&g, IGRAPH_PAGERANK_ALGO_ARPACK, &pprResult, 0,
      igraph_vss_all(),
      directed, 0.85,
      vsAddonsResetNew,
      0, &arpack_options);

    end = steady_clock::now();
    std::cout << "PPR after removal took "
      << duration_cast<std::chrono::seconds>(end - start).count()
      << " secs.\n";

    std::cout << "PPR after removal iterations " << arpack_options.noiter << '\n';
    if (!bDebug)
      print_ppr_vector(&pprResult, ofileAfterRemoval, &vnames, &g, "pprAfterRemoval", &vsAddonsResetNew, true);
    else
      print_ppr_vector(&pprResult, stdout, &vnames, &g, "pprAfterRemoval", &vsAddonsResetNew, true);

    igraph_vector_destroy(&pprResult);
    igraph_vector_ptr_destroy_all(&vptrAddons);
    igraph_vs_destroy(&vsAddonsReset);
    igraph_vs_destroy(&vsAddonsResetNew);
    igraph_vs_destroy(&vsUid);

  } while (numberOfRuns-- > 0);


  fclose(ifile);
  fclose(ofile);
  fclose(degreefile);
  fclose(recallfile);

  end = steady_clock::now();
  std::cout << "The whole run took "
    << duration_cast<std::chrono::seconds>(end - startProgramRun).count()
    << " secs.\n";


  return 0;
#ifdef _MORE_
  ///* Copying a graph */
  //igraph_copy(&g2, &g);
  //print_attributes(&g2);
  //igraph_destroy(&g2);
  //
  ///* Adding vertices */
  //igraph_add_vertices(&g, 3, 0);
  //print_attributes(&g);
  //
  ///* Adding edges */
  //igraph_add_edge(&g, 1, 1);
  //igraph_add_edge(&g, 2, 5);
  //igraph_add_edge(&g, 3, 6);
  //print_attributes(&g);
  //
  ///* Deleting vertices */
  //igraph_delete_vertices(&g, igraph_vss_1(1));
  //igraph_delete_vertices(&g, igraph_vss_1(4));
  //print_attributes(&g);
  //
  ///* Deleting edges */
  //igraph_delete_edges(&g, igraph_ess_1(igraph_ecount(&g) - 1));
  //igraph_delete_edges(&g, igraph_ess_1(0));
  //print_attributes(&g);
  //
  ///* Set graph attributes */
  //SETGAN(&g, "id", 10);
  //if (GAN(&g, "id") != 10) {
  //  return 11;
  //}
  //SETGAS(&g, "name", "toy");
  //if (strcmp(GAS(&g, "name"), "toy")) {
  //  return 12;
  //}
  //
  ///* Delete graph attributes */
  //DELGA(&g, "id");
  //DELGA(&g, "name");
  //igraph_cattribute_list(&g, &gnames, 0, 0, 0, 0, 0);
  //if (igraph_strvector_size(&gnames) != 0) {
  //  return 14;
  //}
  //
  ///* Delete vertex attributes */
  //DELVA(&g, "x");
  //DELVA(&g, "shape");
  //DELVA(&g, "xfact");
  //DELVA(&g, "yfact");
  //igraph_cattribute_list(&g, 0, 0, &vnames, 0, 0, 0);
  //if (igraph_strvector_size(&vnames) != 2) {
  //  return 15;
  //}
  //
  ///* Delete edge attributes */
  //igraph_cattribute_list(&g, 0, 0, 0, 0, &enames, 0);
  //i = igraph_strvector_size(&enames);
  //DELEA(&g, "hook1");
  //DELEA(&g, "hook2");
  //DELEA(&g, "label");
  //igraph_cattribute_list(&g, 0, 0, 0, 0, &enames, 0);
  //if (igraph_strvector_size(&enames) != i - 3) {
  //  return 16;
  //}
  //
  ///* Set vertex attributes */
  //SETVAN(&g, "y", 0, -1);
  //SETVAN(&g, "y", 1, 2.1);
  //if (VAN(&g, "y", 0) != -1 ||
  //  VAN(&g, "y", 1) != 2.1) {
  //  return 17;
  //}
  //SETVAS(&g, "id", 0, "foo");
  //SETVAS(&g, "id", 1, "bar");
  //if (strcmp(VAS(&g, "id", 0), "foo") ||
  //  strcmp(VAS(&g, "id", 1), "bar")) {
  //  return 18;
  //}
  //
  ///* Set edge attributes */
  //SETEAN(&g, "weight", 2, 100.0);
  //SETEAN(&g, "weight", 0, -100.1);
  //if (EAN(&g, "weight", 2) != 100.0 ||
  //  EAN(&g, "weight", 0) != -100.1) {
  //  return 19;
  //}
  //SETEAS(&g, "color", 2, "RED");
  //SETEAS(&g, "color", 0, "Blue");
  //if (strcmp(EAS(&g, "color", 2), "RED") ||
  //  strcmp(EAS(&g, "color", 0), "Blue")) {
  //  return 20;
  //}
  //
  ///* Set vector attributes as vector */
  //igraph_vector_init(&y, igraph_vcount(&g));
  //igraph_vector_fill(&y, 1.23);
  //SETVANV(&g, "y", &y);
  //igraph_vector_destroy(&y);
  //for (i = 0; i<igraph_vcount(&g); i++) {
  //  if (VAN(&g, "y", i) != 1.23) {
  //    return 21;
  //  }
  //}
  //igraph_vector_init_seq(&y, 0, igraph_vcount(&g) - 1);
  //SETVANV(&g, "foobar", &y);
  //igraph_vector_destroy(&y);
  //for (i = 0; i<igraph_vcount(&g); i++) {
  //  if (VAN(&g, "foobar", i) != i) {
  //    return 22;
  //  }
  //}
  //
  //igraph_strvector_init(&id, igraph_vcount(&g));
  //for (i = 0; i<igraph_vcount(&g); i++) {
  //  sprintf_s(str, sizeof(str) - 1, "%li", i);
  //  igraph_strvector_set(&id, i, str);
  //}
  //SETVASV(&g, "foo", &id);
  //igraph_strvector_destroy(&id);
  //for (i = 0; i<igraph_vcount(&g); i++) {
  //  printf("%s ", VAS(&g, "foo", i));
  //}
  //printf("\n");
  //igraph_strvector_init(&id, igraph_vcount(&g));
  //for (i = 0; i<igraph_vcount(&g); i++) {
  //  sprintf_s(str, sizeof(str) - 1, "%li", i);
  //  igraph_strvector_set(&id, i, str);
  //}
  //SETVASV(&g, "id", &id);
  //igraph_strvector_destroy(&id);
  //for (i = 0; i<igraph_vcount(&g); i++) {
  //  printf("%s ", VAS(&g, "id", i));
  //}
  //printf("\n");
  //
  ///* Set edge attributes as vector */
  //igraph_vector_init(&y, igraph_ecount(&g));
  //igraph_vector_fill(&y, 12.3);
  //SETEANV(&g, "weight", &y);
  //igraph_vector_destroy(&y);
  //for (i = 0; i<igraph_ecount(&g); i++) {
  //  if (EAN(&g, "weight", i) != 12.3) {
  //    return 23;
  //  }
  //}
  //igraph_vector_init_seq(&y, 0, igraph_ecount(&g) - 1);
  //SETEANV(&g, "foobar", &y);
  //igraph_vector_destroy(&y);
  //for (i = 0; i<igraph_ecount(&g); i++) {
  //  if (VAN(&g, "foobar", i) != i) {
  //    return 24;
  //  }
  //}
  //
  //igraph_strvector_init(&id, igraph_ecount(&g));
  //for (i = 0; i<igraph_ecount(&g); i++) {
  //  sprintf_s(str, sizeof(str) - 1, "%li", i);
  //  igraph_strvector_set(&id, i, str);
  //}
  //SETEASV(&g, "foo", &id);
  //igraph_strvector_destroy(&id);
  //for (i = 0; i<igraph_ecount(&g); i++) {
  //  printf("%s ", EAS(&g, "foo", i));
  //}
  //printf("\n");
  //igraph_strvector_init(&id, igraph_ecount(&g));
  //for (i = 0; i<igraph_ecount(&g); i++) {
  //  sprintf_s(str, sizeof(str) - 1, "%li", i);
  //  igraph_strvector_set(&id, i, str);
  //}
  //SETEASV(&g, "color", &id);
  //igraph_strvector_destroy(&id);
  //for (i = 0; i<igraph_ecount(&g); i++) {
  //  printf("%s ", EAS(&g, "color", i));
  //}
  //printf("\n");
  //
  ///* Delete all remaining attributes */
  //DELALL(&g);
  //igraph_cattribute_list(&g, &gnames, &gtypes, &vnames, &vtypes, &enames, &etypes);
  //if (igraph_strvector_size(&gnames) != 0 ||
  //  igraph_strvector_size(&vnames) != 0 ||
  //  igraph_strvector_size(&enames) != 0) {
  //  return 25;
  //}
  //
  ///* Destroy */
  //igraph_vector_destroy(&gtypes);
  //igraph_vector_destroy(&vtypes);
  //igraph_vector_destroy(&etypes);
  //igraph_strvector_destroy(&gnames);
  //igraph_strvector_destroy(&vnames);
  //igraph_strvector_destroy(&enames);
  //
  //igraph_destroy(&g);
  //
  //return 0;
#endif
}
